﻿// ParseLib - An open source library for natural language processing.
// Copyright (C) Kais Dukes, 2012. E-mail: kais@kaisdukes.com
// http://parselib.org

using System;
using System.Collections.Generic;
using System.IO;
using ParseLib.Treebanks;

namespace ParseLib.Arabic.Corpora.CorpusReader
{
    public class QuranicReader : IDisposable
    {
        private readonly QuranicArabicCorpus _corpus;
        private StreamReader _reader;
        private bool _header;
        private readonly List<Line> _lines = new List<Line>();

        public QuranicReader(QuranicArabicCorpus corpus, Stream stream)
        {
            _corpus = corpus;
            _reader = new StreamReader(stream);
        }

        public SyntaxGraph ReadGraph()
        {
            // Read graph.
            Line line;
            while (ReadLine(out line))
            {
                // Skip line?
                if (line == null) continue;

                // New graph?
                if (_lines.Count > 0 && line.NodeNumber == 1)
                {
                    var graph = BuildSyntaxGraph();
                    _lines.Add(line);
                    return graph;
                }

                // Add line.
                _lines.Add(line);
            }

            // Last graph.
            return _lines.Count > 0 ? BuildSyntaxGraph() : null;
        }

        public void Dispose()
        {
            if (_reader == null) return;
            _reader.Dispose();
            _reader = null;
        }

        private SyntaxGraph BuildSyntaxGraph()
        {
            // Create graph.
            var graph = new SyntaxGraph(_corpus);

            // Nodes.
            foreach (var line in _lines)
            {
                graph.AddNode(
                    new ArabicNode(line.NodeNumber)
                    {
                        Location = line.Location
                    });
            }

            // Dependencies.
            foreach(var line in _lines)
            {
                var headNumber = line.HeadNumber;
                if (!headNumber.HasValue) continue;
                var node = graph.GetNode(line.NodeNumber - 1);
                node.Head = graph.GetNode(headNumber.Value - 1);
                node.Dependency = line.Dependency;
            }

            // Clear.
            _lines.Clear();
            return graph;
        }

        private bool ReadLine(out Line line)
        {
            // Read line.
            line = null;
            var text = _reader.ReadLine();

            // End of file?
            if (text == null) return false;

            // Empty line or comment?
            if (text.Length == 0 || text[0] == '#')
            {
                return true;
            }

            // Header.
            if (!_header)
            {
                _header = true;
                return true;
            }

            // Line.
            var values = text.Split('\t');
            line = new Line
                       {
                           NodeNumber = int.Parse(values[0]),
                           WordNumber = ReadInt(values[1]),
                           NodeType = ReadNodeType(values[2])
                       };

            // Location.
            if (line.NodeType == NodeType.Phrase)
            {
                var items = values[3].Split('-');
                line.StartNumber = int.Parse(items[0]);
                line.EndNumber = int.Parse(items[1]);
            }
            else if (values[3] != "_")
            {
                line.Location = Location.Parse(values[3]);
            }

            // Text.
            line.Text = ReadString(values[4]);

            // Tag.
            line.PartOfSpeech = _corpus.PartOfSpeechTagset.Build(values[5]);

            // Head.
            line.HeadNumber = ReadInt(values[6]);

            // Dependency.
            line.Dependency = values[7] != "_"
                                  ? _corpus.DependencyTagset.Build(values[7])
                                  : null;

            // Features.
            line.Features = ReadString(values[8]);

            // Add line.
            return true;
        }

        private static int? ReadInt(string value)
        {
            return value != "_" ? int.Parse(value) : (int?) null;
        }

        private static string ReadString(string value)
        {
            return value != "_" ? value : null;
        }

        private static NodeType ReadNodeType(string value)
        {
            switch (value)
            {
                case "T":
                    return NodeType.Terminal;
                case "H":
                    return NodeType.Elided;
                case "P":
                    return NodeType.Phrase;
                case "R":
                    return NodeType.Reference;
                default:
                    throw new ParseLibException("Invalid node type '{0}'.", value);
            }
        }
    }
}